﻿
using Swifter.RW;
using Swifter.Tools;

using System;
using System.Data.Common;

namespace Swifter.Data
{
    sealed class ReadScalarReader : IValueReader, IValueReader<Guid>, IValueReader<DbRowObject>, ITargetedBind, IUsePool
    {
        public const long TargetedId = 0x3707BF7BFE705F90;

        readonly DbDataReader dbDataReader;

        object temp;

        IntPtr tempOwner;

        bool readed;

        int ordinal;

        long ITargetedBind.TargetedId => TargetedId;

        void ITargetedBind.MakeTargetedId()
        {

        }

        public ReadScalarReader(DbDataReader dbDataReader)
        {
            this.dbDataReader = dbDataReader;
        }

        public object DirectRead()
        {
            if (readed || dbDataReader.Read())
            {
                readed = true;

                var value = dbDataReader[ordinal];

                if (value == DBNull.Value)
                    return null;

                return value;
            }

            return null;
        }

        public void ReadArray(IDataWriter<int> valueWriter)
        {
            if (readed)
            {
                var reader = RWHelper.CreateReader(dbDataReader[ordinal]);

                RWHelper.Copy(reader, valueWriter);

                return;
            }

            valueWriter.Initialize();

            if (dbDataReader.Read())
            {
                readed = true;

                int i = 0;

                do
                {
                    valueWriter.OnWriteValue(i, this);

                    ++i;

                } while (dbDataReader.Read());
            }
        }

        public void ReadObject(IDataWriter<string> valueWriter)
        {
            if (valueWriter is IId64DataRW<char> fast1)
            {
                FastReadObject(fast1);
            }
            else if (valueWriter is IDataRW<string> fast2)
            {
                FastReadObject(fast2);
            }
            else
            {
                SlowReadObject(valueWriter);
            }
        }

        public void SlowReadObject(IDataWriter<string> valueWriter)
        {
            if (readed || dbDataReader.Read())
            {
                readed = true;

                valueWriter.Initialize();

                for (ordinal = dbDataReader.FieldCount - 1; ordinal >= 0; --ordinal)
                {
                    valueWriter.OnWriteValue(dbDataReader.GetName(ordinal), this);
                }
            }
        }

        public void FastReadObject(IDataRW<string> valueWriter)
        {
            if (readed || dbDataReader.Read())
            {
                readed = true;

                valueWriter.Initialize();

                if (tempOwner == TypeHelper.GetTypeHandle(valueWriter.ReferenceToken) && temp is string[] names)
                {
                    for (ordinal = names.Length - 1; ordinal >= 0; --ordinal)
                    {
                        valueWriter.OnWriteValue(names[ordinal], this);
                    }
                }
                else
                {
                    tempOwner = TypeHelper.GetTypeHandle(valueWriter.ReferenceToken);

                    temp = names = new string[dbDataReader.FieldCount];

                    for (ordinal = names.Length - 1; ordinal >= 0; --ordinal)
                    {
                        names[ordinal] = dbDataReader.GetName(ordinal);

                        valueWriter.OnWriteValue(names[ordinal], this);
                    }
                }
            }
        }

        public void FastReadObject(IId64DataRW<char> valueWriter)
        {
            if (readed || dbDataReader.Read())
            {
                readed = true;

                valueWriter.Initialize();

                if (tempOwner == TypeHelper.GetTypeHandle(valueWriter.ReferenceToken) && temp is long[] id64s)
                {
                    for (ordinal = id64s.Length - 1; ordinal >= 0; --ordinal)
                    {
                        valueWriter.OnWriteValue(id64s[ordinal], this);
                    }
                }
                else
                {
                    tempOwner = TypeHelper.GetTypeHandle(valueWriter.ReferenceToken);

                    temp = id64s = new long[dbDataReader.FieldCount];

                    for (ordinal = id64s.Length - 1; ordinal >= 0; --ordinal)
                    {
                        id64s[ordinal] = valueWriter.GetId64Ex(dbDataReader.GetName(ordinal));

                        valueWriter.OnWriteValue(id64s[ordinal], this);
                    }
                }
            }
        }

        public bool ReadBoolean()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToBoolean(dbDataReader[ordinal]);
            return default;
        }

        public byte ReadByte()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToByte(dbDataReader[ordinal]);
            return default;
        }

        public char ReadChar()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToChar(dbDataReader[ordinal]);
            return default;
        }

        public DateTime ReadDateTime()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToDateTime(dbDataReader[ordinal]);
            return default;
        }

        public decimal ReadDecimal()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToDecimal(dbDataReader[ordinal]);
            return default;
        }

        public double ReadDouble()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToDouble(dbDataReader[ordinal]);
            return default;
        }

        public short ReadInt16()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToInt16(dbDataReader[ordinal]);
            return default;
        }

        public int ReadInt32()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToInt32(dbDataReader[ordinal]);
            return default;
        }

        public long ReadInt64()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToInt64(dbDataReader[ordinal]);
            return default;
        }

        public sbyte ReadSByte()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToSByte(dbDataReader[ordinal]);
            return default;
        }

        public float ReadSingle()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToSingle(dbDataReader[ordinal]);
            return default;
        }

        public string ReadString()
        {
            if (readed || dbDataReader.Read())
            {
                var value = dbDataReader[ordinal];

                if (value is string st)
                    return st;

                if (value == DBNull.Value)
                    return null;

                return Convert.ToString(value);
            }

            return default;
        }

        public ushort ReadUInt16()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToUInt16(dbDataReader[ordinal]);
            return default;
        }

        public uint ReadUInt32()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToUInt32(dbDataReader[ordinal]);
            return default;
        }

        public ulong ReadUInt64()
        {
            if (readed || dbDataReader.Read())
                return Convert.ToUInt64(dbDataReader[ordinal]);
            return default;
        }

        public Guid ReadValue()
        {
            if (readed || dbDataReader.Read())
            {
                var value = dbDataReader[ordinal];

                if (value is Guid guid)
                {
                    return guid;
                }

                return XConvert<Guid>.Convert(value);
            }

            return default;
        }

        public T? ReadNullable<T>() where T : struct
        {
            if (readed || dbDataReader.Read())
            {
                var value = dbDataReader[ordinal];

                if (value is T t)
                {
                    return t;
                }

                return XConvert<T?>.Convert(value);
            }

            return default;
        }

        DbRowObject IValueReader<DbRowObject>.ReadValue()
        {
            if (readed || dbDataReader.Read())
            {
                readed = true;

                if (!(tempOwner == TypeHelper.GetTypeHandle(typeof(DbRowObject)) && temp is DbRowObjectMap map))
                {
                    tempOwner = TypeHelper.GetTypeHandle(typeof(DbRowObject));

                    map = new DbRowObjectMap(dbDataReader);

                    temp = map;
                }

                return new DbRowObject(map, dbDataReader);
            }

            return default;
        }
    }
}